package com.xxl.job.admin.core.thread;

import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * job monitor instance
 *
 * @author xuxueli 2015-9-1 18:05:56
 */
public class JobFailMonitorHelper {
	private static Logger logger = LoggerFactory.getLogger(JobFailMonitorHelper.class);
	
	private static JobFailMonitorHelper instance = new JobFailMonitorHelper();
	public static JobFailMonitorHelper getInstance(){
		return instance;
	}

	// ---------------------- monitor ----------------------

	private Thread monitorThread;
	private volatile boolean toStop = false;

	/**
	 * 启动一个用于监控作业失败的守护线程。
	 *
	 * 该方法创建并启动一个后台线程，持续检查数据库中记录的失败作业日志。对于每一个失败的作业：
	 * - 如果允许重试，则再次触发执行；
	 * - 如果需要告警，并且满足条件，则发送告警通知。
	 *
	 * 线程每隔10秒进行一次扫描和处理，直到调用停止方法为止。
	 */
	public void start(){
		// 创建一个新的线程用于监控失败的任务
		monitorThread = new Thread(new Runnable() {

			@Override
			public void run() {
				// 持续运行的监控循环，除非 toStop 标志被设置为 true
				while (!toStop) {
					try {
						// 获取最多 1000 条失败任务的日志 ID 列表
						List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000);
						if (failLogIds != null && !failLogIds.isEmpty()) {
							for (long failLogId : failLogIds) {
								// 尝试锁定这条日志（通过更新告警状态从 0 到 -1）
								int lockRet = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, 0, -1);
								if (lockRet < 1) {
									continue;
								}

								// 加载失败任务对应的日志信息与作业配置详情
								XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(failLogId);
								XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId());

								// 第一步：失败重试机制
								// 如果当前任务还有剩余的失败重试次数，则触发一次重试执行
								if (log.getExecutorFailRetryCount() > 0) {
									JobTriggerPoolHelper.trigger(
											log.getJobId(),
											TriggerTypeEnum.RETRY,
											(log.getExecutorFailRetryCount()-1),
											log.getExecutorShardingParam(),
											log.getExecutorParam(),
											null
									);
									String retryMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_type_retry") +"<<<<<<<<<<< </span><br>";
									log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
									XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log);
								}

								// 第二步：失败告警机制
								// 根据任务信息和告警结果确定新的告警状态
								int newAlarmStatus = 0; // 告警状态：0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
								if (info != null) {
									boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log);
									newAlarmStatus = alarmResult ? 2 : 3;
								} else {
									newAlarmStatus = 1;
								}

								// 更新任务日志中的告警状态
								XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, -1, newAlarmStatus);
							}
						}
					} catch (Throwable e) {
						if (!toStop) {
							logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
						}
					}

					try {
						// 每次扫描后休眠 10 秒钟再继续下一轮监控
						TimeUnit.SECONDS.sleep(10);
					} catch (Throwable e) {
						if (!toStop) {
							logger.error(e.getMessage(), e);
						}
					}
				}

				// 监控线程结束时输出日志
				logger.info(">>>>>>>>>>> xxl-job, job fail monitor thread stop");
			}
		});

		// 设置线程为守护线程，防止其阻塞应用关闭
		monitorThread.setDaemon(true);

		// 设置有意义的线程名称，便于调试和日志分析
		monitorThread.setName("xxl-job, admin JobFailMonitorHelper");

		// 启动线程开始执行监控逻辑
		monitorThread.start();
	}


	public void toStop(){
		toStop = true;
		// interrupt and wait
		monitorThread.interrupt();
		try {
			monitorThread.join();
		} catch (Throwable e) {
			logger.error(e.getMessage(), e);
		}
	}

}
